﻿using SanXiao.Game.Common;
using SanXiao.Game.GameCenter.Block;
using SanXiao.Game.GameCenter.SpecialEffects;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SanXiao.Game.GameCenter
{
    public unsafe class GameControlCenter : IDisposable
    {
        private GameObject[,] _set;
        readonly Random _random = new Random();
        private SpecialEffectsObject _special;
        private readonly ImageObject[] _imagesCileck;
        public GameControlCenter(int row, int column, int cellWidth, int cellHeight)
        {
            MaxRow = row;
            MaxColumn = column;
            CellWidth = cellWidth;
            CellHeight = cellHeight;
            RegisterType();

            Bitmap[] bitmaps = { Properties.Resources.shandian1, Properties.Resources.shandian2, Properties.Resources.shandian3, Properties.Resources.shandian4, Properties.Resources.shandian5, Properties.Resources.shandian6, Properties.Resources.shandian7, Properties.Resources.shandian8, Properties.Resources.shandian9 };
            
            _imagesCileck= SpecialEffectsObject.ToImageObjects(bitmaps);
        }
        public int MaxRow { get; set; }
        public int MaxColumn { get; set; }
        public int StartX { get; set; }
        public int StartY { get; set; }
        public int CellWidth { get; set; }
        public int CellHeight { get; set; }
        public float ZoomX { get; set; }
        public float ZoomY { get; set; }
        public void Srart()
        {
            Init();
        }
        public void Init()
        {
            _special = new SpecialEffects2(new Size(CellWidth, CellHeight));

            _set = new GameObject[MaxRow, MaxColumn];
            fixed (GameObject* set = _set)
            {
                for (int i = 0; i < MaxRow; i++)
                {
                    for (int j = 0; j < MaxColumn; j++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        *ptr = CreateBlock(i, j);
                    }
                }
            }
        }
        private void UpdateState(BlockObject gameObject, int row, int column)
        {
            gameObject.StartX = this.StartX;
            gameObject.StartY = this.StartY;
            gameObject.CellWidth = this.CellWidth;
            gameObject.CellHeight = this.CellHeight;
            gameObject.ZoomX = this.ZoomX;
            gameObject.ZoomY = this.ZoomY;
            gameObject.Row = row;
            gameObject.Column = column;
            gameObject.MaxColumn = this.MaxColumn;
            gameObject.MaxRow = this.MaxRow;
            gameObject.UpdateState();

            var block = gameObject as BlockObject;

            var se = _special.Clone() as SpecialEffectsObject;
            block?.SetSpecialEffects(se);
            se.DrawCompleteCallback -= Special_DrawCompleteCallback;
            se.DrawCompleteCallback += Special_DrawCompleteCallback;

            block.ScoreCallback += ScoreCallback;
        }
        private BlockObject CreateBlock(int row, int column)
        {
            var max = GameType.SpecialEffects1.GetHashCode();
            var next = _random.Next(max);
            return CreateBlock((GameType)next, row, column);
        }
        private BlockObject CreateBlock(GameType gameType, int row, int column)
        {
            BlockObject gameObject = null;
            if (!_gameType.ContainsKey(gameType)) return gameObject;

            var type = _gameType[gameType];
            Type[] constructorParams = { typeof(Size) };
            ConstructorInfo constructor = type.GetConstructor(constructorParams);
            Size s = new Size(CellWidth, CellHeight);
            object[] args = { s };
            gameObject = constructor.Invoke(args) as BlockObject;
            UpdateState(gameObject, row, column);
            return gameObject;
        }
        private Dictionary<GameType, Type> _gameType;
        private void RegisterType()
        {
            Type attributeType = typeof(GameTypeAttribute);
            Type gameObjectType = typeof(GameObject);
            _gameType = RegisterType(gameObjectType, attributeType);
        }
        private static Dictionary<GameType, Type> RegisterType(Type baseType, Type attributeType) => RegisterType(Assembly.GetEntryAssembly(), baseType, attributeType);
        private static Dictionary<GameType, Type> RegisterType(Assembly assembly, Type baseType, Type attributeType)
        {
            return assembly.GetTypes().Where(t => t.IsSubclassOf(baseType) && t.GetCustomAttributes(attributeType, false).Any())
                .Select(t =>
                {
                    GameTypeAttribute? sa = t.GetCustomAttribute(attributeType, false) as GameTypeAttribute;

                    return (sa.GameType, t);
                }).ToDictionary(t => t.Item1, t => t.Item2);
        }
        public int Score { get; private set; }
        private void ScoreCallback(int score)
        {
            Score += score;
        }
        bool _isComplete = false;
        private void Special_DrawCompleteCallback(SpecialEffectsObject obj)
        {
            _isComplete = true;
            fixed (GameObject* set = _set)
            {
                // 移除块
                *(set + obj.Row * MaxColumn + obj.Column) = null;
            }
        }
        public void Logic()
        {
            if (_set == null) return;
            if (_isComplete)
            {
                Arrange();
            }
            _isComplete = false;

            fixed (GameObject* set = _set)
            {
                for (int i = 0; i < MaxRow; i++)
                {
                    for (int j = 0; j < MaxColumn; j++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr == null) continue;
                        ptr->Logic(set);
                    }
                }
            }
        }
        private void Arrange()
        {
            fixed (GameObject* set = _set)
            {
                for (int j = 0; j < MaxColumn; j++)
                {
                    int k = 0;
                    for (int i = MaxRow - 1; i >= 0; i--)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr == null)
                        {
                            ++k;
                        }
                        else
                        {
                            if (k == 0)
                                continue;

                            ptr->Row += k;
                            ptr->UpdateState();
                            GameObject* temp = set + ptr->Row * MaxColumn + ptr->Column;
                            *temp = *ptr;
                            *ptr = null;
                        }
                    }
                    for (int i = 0; i < MaxRow; i++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr != null) break;
                        *ptr = CreateBlock(i, j);
                    }
                }
            }
        }

        private int _cilckIndex = -1;
        private Point _clickPoint;
        public void MouseDown(object? sender, MouseEventArgs e)
        {
            if (_set == null) return;

            fixed (GameObject* set = _set)
            {
                int row = (e.Location.Y - this.StartY) / this.CellHeight;
                if (row < 0 || row >= MaxRow) return;
                int col = (e.Location.X - this.StartX) / this.CellWidth;
                if (col < 0 || col >= MaxColumn) return;
                GameObject* ptr = set + row * MaxColumn + col;
                if (*ptr == null) return;
                ptr->MouseDown(set);

                _clickPoint = e.Location;
                _cilckIndex = 0;
            }
        }
        public void Draw(Graphics g, Font font)
        {
            // 得分
            string scoStr = "得分：" + this.Score;
            g.DrawString(scoStr, font, Brushes.OrangeRed, this.StartX + this.MaxColumn / 2 * CellWidth + (int)(100 * ZoomX), this.StartY - (int)(35 * ZoomY));
            if (_set == null) return;
            fixed (GameObject* set = _set)
            {
                for (int i = 0; i < MaxRow; i++)
                {
                    for (int j = 0; j < MaxColumn; j++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr == null) continue;
                        ptr->Draw(g);
                    }
                }
            }

            if (_cilckIndex != -1)
            {
                var img = _imagesCileck[_cilckIndex];
                IntPtr hdc = g.GetHdc();
                const int w = 90;
                const int h = 141;
                int x = _clickPoint.X - w / 2;
                int y = _clickPoint.Y - h;
                GDI32.DrawImageTransparenc(hdc, img.BitmapPtr, x, y, img.Width, img.Height, w, h);
                g.ReleaseHdc(hdc);
                if (++_cilckIndex >= _imagesCileck.Length)
                    _cilckIndex = -1;
            }
        }
        public void SizeChange(int row, int column, int startX, int startY, int cellWidth, int cellHeight, float zoomX, float zoomY)
        {
            this.MaxRow = row;
            this.MaxColumn = column;
            this.StartX = startX;
            this.StartY = startY;
            this.CellWidth = cellWidth;
            this.CellHeight = cellHeight;
            this.ZoomX = zoomX;
            this.ZoomY = zoomY;

            if (_set == null) return;
            fixed (GameObject* set = _set)
            {
                for (int i = 0; i < MaxRow; i++)
                {
                    for (int j = 0; j < MaxColumn; j++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr == null) continue;
                        ptr->SizeChange(row, column, startX, startY, cellWidth, cellHeight, zoomX, zoomY);
                    }
                }
            }
        }
        public void Dispose()
        {
            _special?.Dispose();
            if (_set == null) return;
            fixed (GameObject* set = _set)
            {
                for (int i = 0; i < MaxRow; i++)
                {
                    for (int j = 0; j < MaxColumn; j++)
                    {
                        GameObject* ptr = set + i * MaxColumn + j;
                        if (*ptr == null) continue;
                        ptr->Dispose();
                    }
                }
            }
        }
    }
}
